package com.bicon.botu.file.upload.common;

import com.bicon.botu.core.base.common.Constants;
import com.bicon.botu.core.base.common.entity.BaseResult;
import com.bicon.botu.core.base.common.utils.StringUtils;
import com.bicon.botu.file.upload.vo.CoverTimeDTO;
import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.MultimediaInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import com.bicon.botu.file.upload.vo.VideoProcess;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

/**
 * 视频转码FFmpeg,此任何类型转为MP4格式
 *
 * @Author 李双喜
 * @Date 2017/12/13 10:10
 */
@Slf4j
public class CoverVideo {
    @Value("${server.path}")
    String serverPath;
    @Value("${disk.path}")
    String diskPath;
    private String PATH;

    public CoverVideo(String path) {
        PATH = path;
    }

    public BaseResult<VideoProcess> beginCover(File file, String videoFilePath, String videoCutPath, String realName, String videoType) {
        VideoProcess videoProcess = new VideoProcess();
        if (!checkfile(PATH)) {
            return new BaseResult(404, "系统文件找不到");
        }
        CoverTimeDTO coverTimeDTO = new CoverTimeDTO();
        BaseResult<CoverTimeDTO> baseResult = process(file, videoFilePath, videoCutPath);
        if (baseResult.getCode() == Constants.RESPONSE_CODE_200) {
            coverTimeDTO = baseResult.getData();
        } else {
            return new BaseResult(500, "转码不成功");
        }
        if (StringUtils.isNotEmpty(coverTimeDTO.getVideoFilePath())) {
            videoProcess.setVideoCoverPath(coverTimeDTO.getVideoFilePath());
            videoProcess.setVideoTime(coverTimeDTO.getTimeLong() + "");
            videoProcess.setVideoSize(coverTimeDTO.getVideoSize());
            //截图的图片地址
            String videoCutPicturePath = processImg(file, videoFilePath, videoCutPath, realName, videoType);
            if (StringUtils.isNotEmpty(videoCutPicturePath)) {
                videoProcess.setVideoCutPicturePath(videoCutPicturePath);
                System.out.println("截图成功");
            } else {
                System.out.println("截图不成功");
            }
            PATH = null;
            return new BaseResult(200, "转码成功", videoProcess);
        } else {
            PATH = null;
            return new BaseResult(500, "转码不成功");
        }
    }

    public String processImg(File file, String videoFilePath, String videoCutPath, String realName, String videoType) {
        List<String> commend = new ArrayList<>();
        commend.add("/usr/local/ffmpeg/ffmpeg/install/dir/bin/ffmpeg");
        commend.add("-i");
        commend.add(videoFilePath + ".mp4");
        commend.add("-r");
        commend.add("25");
        commend.add("-ss");
        if (videoType.equals(".mp4")) {
            int i = 10;
            if (this.ReadVideoTime(file) > i) {
                commend.add("00:00:10");
            } else {
                commend.add("00:00:01");
            }
        } else {
            commend.add("00:00:10");
        }
        commend.add("-t");
        commend.add("00:00:01");
        String outFilePath = videoCutPath + realName + ".jpg";
        commend.add(outFilePath);
        Process process = null;
        try {
            String commendString = commend.toString().replace(",", " ");
            StringBuffer stringBuffer = new StringBuffer(commendString);
            StringBuffer buf = stringBuffer.deleteCharAt(0).deleteCharAt(stringBuffer.length() - 1);
            process = Runtime.getRuntime().exec(buf.toString());
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR");
            errorGobbler.start();//  kick  off  stderr
            StreamGobbler outGobbler = new StreamGobbler(process.getInputStream(), "STDOUT");
            outGobbler.start();//  kick  off  stdout
            process.waitFor();
            return outFilePath;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 是否转换成功
     *
     * @returnCoverTimeDTO
     */
    private BaseResult<CoverTimeDTO> process(File file, String videoFilePath, String videoCutPath) {
        int type = checkContentType();
        CoverTimeDTO coverTimeDTO = new CoverTimeDTO();
        if (type == 2) {
            coverTimeDTO.setTimeLong(this.ReadVideoTime(file));
            coverTimeDTO.setVideoFilePath(videoFilePath + ".mp4");
            coverTimeDTO.setVideoSize(this.ReadVideoSize(file));
            if (null != coverTimeDTO) {
                return new BaseResult(200, "success", coverTimeDTO);
            }
        } else if (type == 0) {
            // 直接将文件转为mp4文件
            BaseResult<CoverTimeDTO> baseResult = processMP4(PATH, videoFilePath);
            if (baseResult.getCode() == Constants.RESPONSE_CODE_200) {
                coverTimeDTO = baseResult.getData();
                return new BaseResult(200, "转码成功", coverTimeDTO);
            } else {
                return new BaseResult(500, "转码失败");
            }
        } else if (type == 1) {
            String aviFilePath = null;
            BaseResult<String> baseResult = processAVI(videoFilePath);
            if (baseResult.getCode() == Constants.RESPONSE_CODE_200) {
                aviFilePath = baseResult.getData();
            }
            if (null == aviFilePath) {
                return null;
            } else {
                // 将avi转为mp4
                BaseResult<CoverTimeDTO> dtoBaseResult = processMP4(aviFilePath, videoFilePath);
                if (dtoBaseResult.getCode() == Constants.RESPONSE_CODE_200) {
                    coverTimeDTO = dtoBaseResult.getData();
                    return new BaseResult(200, "转码成功", coverTimeDTO);
                }
            }
        }
        return new BaseResult(400, "此格式不支持");
    }

    /**
     * 检查视频类型能否被ffmpeg转码
     *
     * @return
     */
    private int checkContentType() {
        String type = PATH.substring(PATH.lastIndexOf(".") + 1, PATH.length())
                .toLowerCase();
        // ffmpeg能解析的格式：（asx，asf，mpg，wmv，3gp，mp4，mov，avi，flv等）
        if (type.equals("mp4")) {
            return 2;
        } else if (type.equals("avi")) {
            return 0;
        } else if (type.equals("mpg")) {
            return 0;
        } else if (type.equals("wmv")) {
            return 0;
        } else if (type.equals("3gp")) {
            return 0;
        } else if (type.equals("mov")) {
            return 0;
        } else if (type.equals("asf")) {
            return 0;
        } else if (type.equals("asx")) {
            return 0;
        } else if (type.equals("flv")) {
            return 0;
        }
        // 对ffmpeg无法解析的文件格式(wmv9，rm，rmvb等),
        // 可以先用别的工具（mencoder）转换为avi(ffmpeg能解析的)格式.
        else if (type.equals("wmv9")) {
            return 1;
        } else if (type.equals("rm")) {
            return 1;
        } else if (type.equals("rmvb")) {
            return 1;
        }
        return 9;
    }

    private boolean checkfile(String path) {
        File file = new File(path);
        if (file.isFile()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 对ffmpeg无法解析的文件格式(wmv9，rm，rmvb等),
     * 可以先用别的工具（mencoder）转换为avi(ffmpeg能解析的)格式.
     *
     * @param
     * @return
     */
    private BaseResult processAVI(String videoFilePath) {
        List commend = new ArrayList();
        commend.add("mencoder");
        commend.add(PATH);
        commend.add("-vf");
        commend.add("crop=714:548:0:14");
        commend.add("-oac");
        commend.add("copy");
        commend.add("-ovc");
        commend.add("lavc");
        commend.add("-lavcopts");
        commend.add("vcodec=mpeg4:mbd=2:trell:autoaspect");
        commend.add("-o");
        commend.add(videoFilePath + ".avi");
        Process process;
        try {
            String commendString = commend.toString().replace(",", " ");
            StringBuffer stringBuffer = new StringBuffer(commendString);
            StringBuffer buf = stringBuffer.deleteCharAt(0).deleteCharAt(stringBuffer.length() - 1);
            process = Runtime.getRuntime().exec(buf.toString());
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR");
            errorGobbler.start();//  kick  off  stderr
            StreamGobbler outGobbler = new StreamGobbler(process.getInputStream(), "STDOUT");
            outGobbler.start();//  kick  off  stdout
            process.waitFor();
            process.destroy();
            return new BaseResult(200, "success", videoFilePath + ".avi");
        } catch (Exception e) {
            e.printStackTrace();
            return new BaseResult(500, "转码不成功");
        }
    }

    /**
     * ffmpeg能解析的格式：（asx，asf，mpg，wmv，3gp，mp4，mov，avi，flv等）
     *
     * @param oldFilePath 此源文件
     * @return
     */
    private BaseResult<CoverTimeDTO> processMP4(String oldFilePath, String videoFilePath) {

        if (!checkfile(oldFilePath)) {
            return new BaseResult(500, "不是一个文件");
        }
        CoverTimeDTO coverTimeDTO = new CoverTimeDTO();
        List commend = new ArrayList();
        commend.add("/usr/local/ffmpeg/ffmpeg/install/dir/bin/ffmpeg");
        commend.add("-i");
        commend.add(oldFilePath);
        commend.add("-c:v");
        commend.add("libx264");
        commend.add("-crf");
        commend.add("24");
        commend.add("-crf");
        commend.add("-ab");
        commend.add("64");
        commend.add("-acodec");
        commend.add("mp3");
        commend.add("-ac");
        commend.add("2");
        commend.add("-ar");
        commend.add("22050");
        commend.add("-b");
        commend.add("230");
        commend.add("-r");
        commend.add("24");
        commend.add("-y");
        commend.add(videoFilePath + ".mp4");
        Process process;
        try {
            String commendString = commend.toString().replace(",", " ");
            StringBuffer stringBuffer = new StringBuffer(commendString);
            StringBuffer buf = stringBuffer.deleteCharAt(0).deleteCharAt(stringBuffer.length() - 1);
            process = Runtime.getRuntime().exec(buf.toString());
            StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR");
            errorGobbler.start();//  kick  off  stderr
            StreamGobbler outGobbler = new StreamGobbler(process.getInputStream(), "STDOUT");
            outGobbler.start();//  kick  off  stdout
            process.waitFor();
            process.destroy();
//            ProcessBuilder builder = new ProcessBuilder();
//            builder.command(commend);
//            Process p = builder.start();
//            //从输入流中读取视频信息
//            BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
//            StringBuffer sb = new StringBuffer();
//            String line;
//            while ((line = br.readLine()) != null) {
//                sb.append(line);
//            }
//            //从视频信息中解析时长
//            String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";
//            Pattern pattern = Pattern.compile(regexDuration);
//            Matcher m = pattern.matcher(sb.toString());
//            if (m.find()) {
//                int time = getTimelen(m.group(1));
//                coverTimeDTO.setTimeLong(time);
//            }
//            br.close();
//            doWaitFor(p);
//            p.destroy();
            boolean delete = deleteVideoFile(oldFilePath);
            if (delete) {
                coverTimeDTO.setVideoFilePath(videoFilePath + ".mp4");
                coverTimeDTO.setVideoSize(this.ReadVideoSize(new File(videoFilePath + ".mp4")));
                return new BaseResult(200, "success", coverTimeDTO);
            }
            return new BaseResult(500, "源文件删除不成功");
        } catch (Exception e) {
            e.printStackTrace();
            return new BaseResult(500, "server error");
        }
    }

//    public int doWaitFor(Process p) {
//        InputStream in = null;
//        InputStream err = null;
//        int exitValue = -1;
//        try {
//            in = p.getInputStream();
//            err = p.getErrorStream();
//            boolean finished = false;
//            while (!finished) {
//                try {
//                    while (in.available() > 0) {
//                        Character c = new Character((char) in.read());
//                    }
//                    while (err.available() > 0) {
//                        Character c = new Character((char) err.read());
//                    }
//                    exitValue = p.exitValue();
//                    finished = true;
//
//                } catch (IllegalThreadStateException e) {
//                    Thread.currentThread().sleep(500);
//                }
//            }
//        } catch (Exception e) {
//        } finally {
//            try {
//                if (in != null) {
//                    in.close();
//                }
//            } catch (IOException e) {
//            }
//            if (err != null) {
//                try {
//                    err.close();
//                } catch (IOException e) {
//                }
//            }
//        }
//        return exitValue;
//    }

    /**
     * 删除视频源文件
     *
     * @param filepath
     */
    public boolean deleteVideoFile(String filepath) {
        File file = new File(filepath);
        if (PATH.equals(filepath)) {
            if (file.delete()) {
                return true;
            }
        } else {
            if (file.delete()) {
                return true;
            }
            File filedelete2 = new File(PATH);
            if (filedelete2.delete()) {
                return true;
            }
        }
        return false;
    }

    /**
     * 计算视频转换时长
     *
     * @param ms
     * @return
     */
//    public String sumTime(long ms) {
//        int ss = 1000;
//        long mi = ss * 60;
//        long hh = mi * 60;
//        long dd = hh * 24;
//
//        long day = ms / dd;
//        long hour = (ms - day * dd) / hh;
//        long minute = (ms - day * dd - hour * hh) / mi;
//        long second = (ms - day * dd - hour * hh - minute * mi) / ss;
//        long milliSecond = ms - day * dd - hour * hh - minute * mi - second
//                * ss;
//
//        String strDay = day < 10 ? "0" + day + "天" : "" + day + "天";
//        String strHour = hour < 10 ? "0" + hour + "小时" : "" + hour + "小时";
//        String strMinute = minute < 10 ? "0" + minute + "分" : "" + minute + "分";
//        String strSecond = second < 10 ? "0" + second + "秒" : "" + second + "秒";
//        String strMilliSecond = milliSecond < 10 ? "0" + milliSecond : ""
//                + milliSecond;
//        strMilliSecond = milliSecond < 100 ? "0" + strMilliSecond + "毫秒" : ""
//                + strMilliSecond + " 毫秒";
//        return strDay + " " + strHour + ":" + strMinute + ":" + strSecond + " "
//                + strMilliSecond;
//    }

//    //格式:"00:00:10.68"
//    private static int getTimelen(String timelen) {
//        int min = 0;
//        String strs[] = timelen.split(":");
//        if (strs[0].compareTo("0") > 0) {
//            min += Integer.valueOf(strs[0]) * 60 * 60;//秒
//        }
//        if (strs[1].compareTo("0") > 0) {
//            min += Integer.valueOf(strs[1]) * 60;
//        }
//        if (strs[2].compareTo("0") > 0) {
//            min += Math.round(Float.valueOf(strs[2]));
//        }
//        return min;
//    }

    /**
     * 获取视频时长（在Mp4不用转换时下）
     *
     * @param source
     * @return
     */
    private Integer ReadVideoTime(File source) {
        Encoder encoder = new it.sauronsoftware.jave.Encoder();
        long ls = 0;
        try {
            MultimediaInfo m = encoder.getInfo(source);
            ls = m.getDuration() / 1000;
            String regexVideo = "Video: (.*?), (.*?), (.*?)[,\\s]";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (int) ls;
    }

//    /**
//     * 获取视频的宽
//     */
//    private Integer ReadVideoWidth(File source) {
//        Encoder encoder = new Encoder();
//        int ls = 0;
//        try {
//            MultimediaInfo m = encoder.getInfo(source);
//            ls = m.getVideo().getSize().getWidth();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return ls;
//    }

//    /**
//     * 获取视频的高
//     */
//    private Integer ReadVideoHeight(File source) {
//        Encoder encoder = new Encoder();
//        int ls = 0;
//        try {
//
//            MultimediaInfo m = encoder.getInfo(source);
//            ls = m.getVideo().getSize().getHeight();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return ls;
//    }

    /**
     * 获取视频大小
     *
     * @param source
     * @return
     */
    private String ReadVideoSize(File source) {
        FileChannel fc = null;
        String size = "";
        try {
            @SuppressWarnings("resource")
            FileInputStream fis = new FileInputStream(source);
            fc = fis.getChannel();
            BigDecimal fileSize = new BigDecimal(fc.size());
            size = fileSize.divide(new BigDecimal(1048576), 2, RoundingMode.HALF_UP) + "MB";
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != fc) {
                try {
                    fc.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return size;
    }
//
//    private String videoWidthHeight(String path) {
//        IContainer container = IContainer.make();
//        int result = container.open(path, IContainer.Type.READ, null);
//        if (result < 0) {
//            return null;
//        }
//        for (int i = 0; i < container.getNumStreams(); i++) {
//            IStream stream = container.getStream(i);
//            IStreamCoder coder = stream.getStreamCoder();
//            if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
//                return coder.getWidth() + "*" + coder.getHeight();
//            }
//        }
//        return null;
//    }


//    public static Map<String, Integer> getImageWH(byte[] bytes) {
//        Map<String, Integer> map = new HashMap<String, Integer>();
//        if (bytes == null) {
//            map.put("w", 0);
//            map.put("h", 0);
//            return map;
//        }
//        try {
//            ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
//            BufferedImage bi = ImageIO.read(stream);
//            int srcWidth = bi.getWidth(); // 源图宽度
//            int srcHeight = bi.getHeight(); // 源图高度
//            map.put("w", srcWidth);
//            map.put("h", srcHeight);
//        } catch (Exception ex) {
//            ex.printStackTrace();
//            map.put("w", 0);
//            map.put("h", 0);
//            return map;
//        }
//        return map;
//    }
}
